/*- * See the file LICENSE for redistribution information. * * Copyright (c) 2002-2006 * Sleepycat Software. All rights reserved. * * $Id: DIN.java,v 1.1 2006/05/06 09:00:15 ckaestne Exp $ */ package com.sleepycat.je.tree; import java.nio.ByteBuffer; import java.util.Comparator; import com.sleepycat.je.DatabaseException; import com.sleepycat.je.cleaner.Cleaner; import com.sleepycat.je.dbi.DatabaseId; import com.sleepycat.je.dbi.DatabaseImpl; import com.sleepycat.je.dbi.DbConfigManager; import com.sleepycat.je.dbi.MemoryBudget; import com.sleepycat.je.log.LogEntryType; import com.sleepycat.je.log.LogException; import com.sleepycat.je.log.LogManager; import com.sleepycat.je.log.LogUtils; import com.sleepycat.je.txn.LockResult; import com.sleepycat.je.txn.Locker; /** * An DIN represents an Duplicate Internal Node in the JE tree. */ public final class DIN extends IN { private static final String BEGIN_TAG = "<din>"; private static final String END_TAG = "</din>"; /** * Full key for this set of duplicates. */ private byte[] dupKey; /** * Reference to DupCountLN which stores the count. */ private ChildReference dupCountLNRef; /** * Create an empty DIN, with no node id, to be filled in from the log. */ public DIN() { super(); dupCountLNRef = new ChildReference(); init(null, Key.EMPTY_KEY, 0, 0); } /** * Create a new DIN. */ public DIN(DatabaseImpl db, byte[] identifierKey, int capacity, byte[] dupKey, ChildReference dupCountLNRef, int level) { super(db, identifierKey, capacity, level); this.dupKey = dupKey; this.dupCountLNRef = dupCountLNRef; initMemorySize(); // init after adding Dup Count LN. */ } /* Duplicates have no mask on their levels. */ protected int generateLevel(DatabaseId dbId, int newLevel) { return newLevel; } /** * Create a new DIN. Need this because we can't call newInstance() * without getting a 0 node. */ protected IN createNewInstance(byte[] identifierKey, int maxEntries, int level) { return new DIN(getDatabase(), identifierKey, maxEntries, dupKey, dupCountLNRef, level); } /** * Return the key for this duplicate set. */ public byte[] getDupKey() { return dupKey; } /** * Get the key (dupe or identifier) in child that is used to locate * it in 'this' node. */ public byte[] getChildKey(IN child) throws DatabaseException { return child.getIdentifierKey(); } /* * A DIN uses the dupTree key in its searches. */ public byte[] selectKey(byte[] mainTreeKey, byte[] dupTreeKey) { return dupTreeKey; } /** * Return the key for navigating through the duplicate tree. */ public byte[] getDupTreeKey() { return getIdentifierKey(); } /** * Return the key for navigating through the main tree. */ public byte[] getMainTreeKey() { return dupKey; } public ChildReference getDupCountLNRef() { return dupCountLNRef; } public DupCountLN getDupCountLN() throws DatabaseException { return (DupCountLN) dupCountLNRef.fetchTarget(getDatabase(), this); } /* * All methods that modify the dup count LN must adjust memory sizing. */ /** * Assign the Dup Count LN. */ void setDupCountLN(ChildReference dupCountLNRef) { updateMemorySize(this.dupCountLNRef, dupCountLNRef); this.dupCountLNRef = dupCountLNRef; } /** * Assign the Dup Count LN node. Does not dirty the DIN. */ public void updateDupCountLN(Node target) { long oldSize = getEntryInMemorySize(dupCountLNRef.getKey(), dupCountLNRef.getTarget()); dupCountLNRef.setTarget(target); long newSize = getEntryInMemorySize(dupCountLNRef.getKey(), dupCountLNRef.getTarget()); updateMemorySize(oldSize, newSize); } /** * Update Dup Count LN. */ public void updateDupCountLNRefAndNullTarget(long newLsn) { setDirty(true); long oldSize = getEntryInMemorySize(dupCountLNRef.getKey(), dupCountLNRef.getTarget()); dupCountLNRef.setTarget(null); dupCountLNRef.setLsn(newLsn); long newSize = getEntryInMemorySize(dupCountLNRef.getKey(), dupCountLNRef.getTarget()); updateMemorySize(oldSize, newSize); } /** * Update dup count LSN. */ public void updateDupCountLNRef(long newLsn) { setDirty(true); dupCountLNRef.setLsn(newLsn); } /** * @return true if this node is a duplicate-bearing node type, false * if otherwise. */ public boolean containsDuplicates() { return true; } /* Never true for a DIN. */ public boolean isDbRoot() { return false; } /** * Return the comparator function to be used for DINs. This is * the user defined duplicate comparison function, if defined. */ public final Comparator getKeyComparator() { return getDatabase().getDuplicateComparator(); } /** * Increment or decrement the DupCountLN, log the updated LN, and update * the lock result. * * Preconditions: This DIN is latched and the DupCountLN is write locked. * Postconditions: Same as preconditions. */ public void incrementDuplicateCount(LockResult lockResult, byte[] key, Locker locker, boolean increment) throws DatabaseException { /* Increment/decrement the dup count and update its owning DIN. */ long oldLsn = dupCountLNRef.getLsn(); lockResult.setAbortLsn(oldLsn, dupCountLNRef.isKnownDeleted()); DupCountLN dupCountLN = getDupCountLN(); if (increment) { dupCountLN.incDupCount(); } else { dupCountLN.decDupCount(); assert dupCountLN.getDupCount() >= 0; } DatabaseImpl db = getDatabase(); long newCountLSN = dupCountLN.log (db.getDbEnvironment(), db.getId(), key, oldLsn, locker); updateDupCountLNRef(newCountLSN); } /** * Count up the memory usage attributable to this node alone. LNs children * are counted by their BIN/DIN parents, but INs are not counted by * their parents because they are resident on the IN list. */ protected long computeMemorySize() { long size = super.computeMemorySize(); if (dupCountLNRef != null) { size += getEntryInMemorySize(dupCountLNRef.getKey(), dupCountLNRef.getTarget()); } /* XXX Need to update size when changing the dupKey. if (dupKey != null && dupKey.getKey() != null) { size += MemoryBudget.byteArraySize(dupKey.getKey().length); } */ return size; } /* Called once at environment startup by MemoryBudget. */ public static long computeOverhead(DbConfigManager configManager) throws DatabaseException { /* * Overhead consists of all the fields in this class plus the * entry arrays in the IN class. */ return MemoryBudget.DIN_FIXED_OVERHEAD + IN.computeArraysOverhead(configManager); } protected long getMemoryOverhead(MemoryBudget mb) { return mb.getDINOverhead(); } /* * Depth first search through a duplicate tree looking for an LN that * has nodeId. When we find it, set location.bin and index and return * true. If we don't find it, return false. * * No latching is performed. */ boolean matchLNByNodeId(TreeLocation location, long nodeId) throws DatabaseException { for (int i = 0; i < getNEntries(); i++) { Node n = fetchTarget(i); if (n != null) { boolean ret = n.matchLNByNodeId(location, nodeId); if (ret) { return true; } } } return false; } /* * DbStat support. */ void accumulateStats(TreeWalkerStatsAccumulator acc) { acc.processDIN(this, new Long(getNodeId()), getLevel()); } /* * Logging Support */ /** * @see IN#getLogType */ public LogEntryType getLogType() { return LogEntryType.LOG_DIN; } /** * Handles lazy migration of DupCountLNs prior to logging a DIN. See * BIN.logInternal for more information. */ protected long logInternal(LogManager logManager, boolean allowDeltas, boolean isProvisional, boolean proactiveMigration, IN parent) throws DatabaseException { /* Allow the cleaner to migrate the DupCountLN before logging. */ if (dupCountLNRef != null) { Cleaner cleaner = getDatabase().getDbEnvironment().getCleaner(); cleaner.lazyMigrateDupCountLN (this, dupCountLNRef, proactiveMigration); } return super.logInternal (logManager, allowDeltas, isProvisional, proactiveMigration, parent); } /** * @see IN#getLogSize */ public int getLogSize() { int size = super.getLogSize(); // ancestors size += LogUtils.getByteArrayLogSize(dupKey);// identifier key size += LogUtils.getBooleanLogSize(); // dupCountLNRef null flag if (dupCountLNRef != null) { size += dupCountLNRef.getLogSize(); } return size; } /** * @see IN#writeToLog */ public void writeToLog(ByteBuffer logBuffer) { // ancestors super.writeToLog(logBuffer); // identifier key LogUtils.writeByteArray(logBuffer, dupKey); /* DupCountLN */ boolean dupCountLNRefExists = (dupCountLNRef != null); LogUtils.writeBoolean(logBuffer, dupCountLNRefExists); if (dupCountLNRefExists) { dupCountLNRef.writeToLog(logBuffer); } } /** * @see IN#readFromLog */ public void readFromLog(ByteBuffer itemBuffer, byte entryTypeVersion) throws LogException { // ancestors super.readFromLog(itemBuffer, entryTypeVersion); // identifier key dupKey = LogUtils.readByteArray(itemBuffer); /* DupCountLN */ boolean dupCountLNRefExists = LogUtils.readBoolean(itemBuffer); if (dupCountLNRefExists) { dupCountLNRef.readFromLog(itemBuffer, entryTypeVersion); } else { dupCountLNRef = null; } } /** * DINS need to dump their dup key */ protected void dumpLogAdditional(StringBuffer sb) { super.dumpLogAdditional(sb); sb.append(Key.dumpString(dupKey, 0)); if (dupCountLNRef != null) { dupCountLNRef.dumpLog(sb, true); } } /* * Dumping */ public String beginTag() { return BEGIN_TAG; } public String endTag() { return END_TAG; } /** * For unit test support: * @return a string that dumps information about this DIN, without */ public String dumpString(int nSpaces, boolean dumpTags) { StringBuffer sb = new StringBuffer(); if (dumpTags) { sb.append(TreeUtils.indent(nSpaces)); sb.append(beginTag()); sb.append('\n'); } sb.append(TreeUtils.indent(nSpaces+2)); sb.append("<dupkey>"); sb.append(dupKey == null ? "" : Key.dumpString(dupKey, 0)); sb.append("</dupkey>"); sb.append('\n'); if (dupCountLNRef == null) { sb.append(TreeUtils.indent(nSpaces+2)); sb.append("<dupCountLN/>"); } else { sb.append(dupCountLNRef.dumpString(nSpaces + 4, true)); } sb.append('\n'); sb.append(super.dumpString(nSpaces, false)); if (dumpTags) { sb.append(TreeUtils.indent(nSpaces)); sb.append(endTag()); } return sb.toString(); } public String toString() { return dumpString(0, true); } public String shortClassName() { return "DIN"; } }